Skip to content

feat: multi-buyer identity support in AI Credits widget - #136

Open
blueogin with Copilot wants to merge 14 commits into
mainfrom
copilot/multi-buyer-support-ai-credits-widget-again
Open

feat: multi-buyer identity support in AI Credits widget#136
blueogin with Copilot wants to merge 14 commits into
mainfrom
copilot/multi-buyer-support-ai-credits-widget-again

Conversation

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

A payer wallet was limited to a single deterministic buyer identity. This adds full multi-buyer identity management: create additional derived buyers, import via private key, register address-only (view/consent) buyers, switch between them, and deep-link assign a partner buyer via URL params.

Runtime contract & session model

  • BuyerRecord type with type: 'derived' | 'imported' | 'address-only', derivationIndex, and label
  • payerSession now stores buyers: BuyerRecord[] + activeBuyerAddress; old single-buyer sessions auto-migrate on first read
  • State gains buyers and activeBuyerAddress; buyerPubKey/buyerPrvKey remain as computed fields from the active buyer

Derivation

  • buildBuyerKeyMessage(address, index) — index 0 preserves the exact legacy message for backward compatibility; index > 0 appends (buyer N)

New adapter actions

Action Behavior
createBuyer Signs wallet message at next derivation index, adds to session
selectBuyer(address) Switches active buyer, resets consent/chain state
importBuyerFromPrivateKey(key) Validates 0x…64hex, derives address, registers as imported
selectBuyerByAddress(address) Registers address-only buyer; sign-required actions disabled
applyDeepLinkBuyer(address, sig) Validates ?buyerAddress=&buyerSignature= URL params on mount, registers partner buyer

UI

  • BuyerOperatorCard: buyer selector list (visible when >1 buyer), "New Buyer" button, collapsible "Import Key" / "Watch Address" panel; address-only buyers show a "view only" badge and Sign Consent is disabled
  • HistoryTab: BuyerFilterSelect dropdown fed via new knownBuyers prop; defaults to active buyer on history tab open
  • useAiCreditsHistory: buyerAddressFilter state + setBuyerAddressFilter action; client-side filtering on entry.buyerAddress

Stories & tests

Three new QA fixtures (MultiBuyerManage, AddressOnlyBuyer, MultiBuyerHistory) and four Playwright smoke tests covering the new flows.

Copilot AI requested review from Copilot and removed request for Copilot July 30, 2026 19:25
Copilot AI linked an issue Jul 30, 2026 that may be closed by this pull request
24 tasks
Co-authored-by: blueogin <43612769+blueogin@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot July 30, 2026 19:40
Copilot AI changed the title [WIP] Add multi-buyer support in AI Credits widget feat: multi-buyer identity support in AI Credits widget Jul 30, 2026
Copilot AI requested a review from blueogin July 30, 2026 19:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands the AI Credits widget runtime/session model from a single deterministic buyer identity to a multi-buyer identity system per payer wallet, adding UI affordances to create/import/watch buyers, switching active buyer context, and filtering history by buyer.

Changes:

  • Introduces BuyerRecord[] + activeBuyerAddress into runtime state/session (with legacy session migration) and adds new adapter actions for buyer lifecycle (create/select/import/watch/deep-link).
  • Updates Manage + History UI to support buyer selection and buyer-based history filtering.
  • Adds Storybook QA fixtures and Playwright smoke coverage for multi-buyer flows.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/widgets/ai-credits-widget/states.spec.ts Adds Playwright smoke tests + screenshots for multi-buyer UI states.
packages/ai-credits-widget/src/widgetRuntimeContract.ts Extends adapter state/actions for multi-buyer support and re-exports BuyerRecord.
packages/ai-credits-widget/src/useAiCreditsHistory.ts Adds buyer filter state/action and client-side filtering by entry.buyerAddress.
packages/ai-credits-widget/src/payerSession.ts Replaces legacy single-buyer session fields with buyers[] + activeBuyerAddress and migration logic.
packages/ai-credits-widget/src/index.ts Exports the new buyer-related types for consumers.
packages/ai-credits-widget/src/components/manage/BuyerOperatorCard.tsx Adds buyer selector UI and import/watch panel; disables signing for address-only buyers.
packages/ai-credits-widget/src/components/history/HistoryTab.tsx Adds buyer filter dropdown fed by known buyers.
packages/ai-credits-widget/src/buyerKeyDerivation.ts Makes derivation message index-aware while preserving index 0 legacy message.
packages/ai-credits-widget/src/AiCreditsWidget.tsx Wires knownBuyers to History UI and defaults history filter to active buyer.
packages/ai-credits-widget/src/adapter.ts Implements multi-buyer session wiring + new actions (create/select/import/watch/deep-link) and URL param parsing.
examples/storybook/src/stories/helpers/aiCreditsWidgetStories.tsx Adds multi-buyer QA fixture stories.
examples/storybook/src/stories/ai-credits-widget/AiCreditsWidgetQA.stories.tsx Exposes new QA fixtures in Storybook.
Suppressed comments (3)

packages/ai-credits-widget/src/adapter.ts:894

  • Deep-link signature validation currently accepts any non-empty hex string (/^0x[0-9a-fA-F]{2,}$/), so malformed/too-short signatures will be treated as “valid”. At minimum, require a 64- or 65-byte signature (EIP-2098 compact or standard) before accepting URL params.
      if (!/^0x[0-9a-fA-F]{2,}$/.test(trimmedSignature)) {
        setState((prev) =>
          withDerivedStatus(prev, { error: 'Deep-link buyer signature is invalid' }, true),
        )
        return

packages/ai-credits-widget/src/adapter.ts:1457

  • Deep-link parsing runs before a payer wallet is connected. When buyerAddress/buyerSignature are present but address is still null, handleApplyDeepLinkBuyer sets an error (“Connect your wallet…”) on mount. Since the effect re-runs after connect anyway, defer applying deep-link params until address is available to avoid a spurious error flash.
  useEffect(() => {
    if (typeof window === 'undefined') return
    const params = new URLSearchParams(window.location.search)
    const urlBuyerAddress = params.get('buyerAddress')
    const urlBuyerSignature = params.get('buyerSignature')

packages/ai-credits-widget/src/components/history/HistoryTab.tsx:524

  • Options in BuyerFilterSelect have role="option" but don’t indicate selection via aria-selected, which is part of the listbox pattern. This makes it harder for screen readers to announce which buyer filter is currently selected.
              tag="button"
              role="option"
              paddingHorizontal="$3"
              paddingVertical="$2"
              cursor="pointer"

Comment thread packages/ai-credits-widget/src/adapter.ts Outdated
Comment thread packages/ai-credits-widget/src/buyerKeyDerivation.ts Outdated
Comment thread packages/ai-credits-widget/src/adapter.ts Outdated
Comment thread packages/ai-credits-widget/src/components/history/HistoryTab.tsx
blueogin added 2 commits July 31, 2026 15:19
…ts widget

- Added support for deep link parameters, including buyer address and operator signature, to facilitate buyer registration via NCDI deep links.
- Introduced validation functions for buyer address and operator signature.
- Enhanced state management to handle operator consent and session updates based on deep link data.
- Updated relevant components and types to accommodate new deep link functionality, ensuring a seamless user experience when applying deep links.
- Introduced new functions for managing deep link parameters, including storage and retrieval from local storage.
- Updated error handling to provide clearer messages when deep link parameters are invalid or missing.
- Improved state management to reflect deep link status and errors in the UI, ensuring a better user experience.
- Added a fallback mechanism for deep link processing, enhancing robustness in various scenarios.
…ng and retrieval

- Added a buyers array to track buyer addresses and consent timestamps in the state.
- Implemented getBuyerAddresses method to retrieve buyer addresses for a given payer.
- Updated submitOperatorConsent to include payer information and manage buyer consent state effectively.
- Refactored state management to ensure consistency and clarity in buyer handling.

@sirpy sirpy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blueogin what are all these MOCK_DELAY_MS?

@blueogin

blueogin commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@sirpy
Fake 600ms delay in the mock backend so UI loading states show up in Storybook.

…e buyer address management

- Implemented discoverBuyers action to track and manage buyer addresses dynamically.
- Enhanced the adapter to resolve local buyer addresses and select preferred buyers based on history and session data.
- Updated the widget to trigger buyer discovery during history retrieval, improving user experience.
- Refactored state management to include known buyers and streamline buyer address handling across components.
@sirpy

sirpy commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@blueogin is mock delay set to 0 in non storybook env? it should. it shouldnt be in production

@sirpy sirpy left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i dont understand the latest changes.
buyers is very simple
user add buyer via private key, or generate one based on signature or add it via url params.
when user add buyer it is saved on localstorage.
this is the list of user buyers.
that's it. simple.

…and stories

- Introduced derivedBuyerAddress to the AiCreditsWidgetAdapterState for improved buyer address handling.
- Updated relevant components and stories to utilize derivedBuyerAddress, enhancing the management of buyer selections.
- Added utility function to determine if the selected buyer is derived, improving the logic in purchase flow handling.
- Introduced `operatorConsentPending` to the `AiCreditsWidgetAdapterState` for tracking the consent submission process.
- Updated relevant components and hooks to handle the new state, improving user feedback during the operator consent flow.
- Enhanced UI elements to reflect the pending state, ensuring a smoother user experience during consent submission.
- Adjusted Storybook stories to demonstrate the new consent pending behavior.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Multi-buyer support in AI Credits widget

4 participants